package cn.bigfire.wzdj.util.protode;


import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeMap;

public class DataBuffer {

    byte[] buffer;
    int byteLen;
    int offset;

    public DataBuffer(byte[] bytes) {
        this.buffer = bytes;
        this.byteLen = bytes.length;
        offset = 0;
    }

    public static DataBuffer of(byte[] bytes) {
        return new DataBuffer(bytes);
    }

    public byte[] getBuffer() {
        return buffer;
    }

    public void setBuffer(byte[] buffer) {
        this.buffer = buffer;
    }

    public int getByteLen() {
        return byteLen;
    }

    public void setByteLen(int byteLen) {
        this.byteLen = byteLen;
    }

    public int getOffset() {
        return offset;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }


    public boolean checkTag(int tag, int oldFieldNumber) {
        int fieldNumber_new = bitFieldNumber(tag);
        int wireTypeVal = bitWireType(tag);
        if (fieldNumber_new < oldFieldNumber) {//乱序了，后面的序号不应该大于前面的序号
            return false;
        }
        if (fieldNumber_new < 1) {
            return false;
        }
        return WireType.hasWireType(wireTypeVal);
    }

    public boolean isObjData(byte[] afterBytes) {//一段数据进来了
        int tag = afterBytes[0];//
        int fieldNumber = bitFieldNumber(tag);//5Bit
        int wireTypeVal = bitWireType(tag);//3Bit
        if (fieldNumber < 1) {//>0
            return false;//乱序
        }
        WireType wireType = WireType.valueOf(wireTypeVal);
        //这两钟类型已经废弃了，所以一定不是protobuf对象
        if (WireType.START.equals(wireType)||WireType.END.equals(wireType)){
            return false;
        }
        // VINT,BIT64,OBJ_STR,BIT32 如何是这4个
        //该读取len字段了，如果长度对不上，那说明也不是合法的嵌套对象
        byte[] LVBytes = Arrays.copyOfRange(afterBytes, 1, afterBytes.length);
        if (LVBytes.length == 0) {
            return false;
        }
        int vLen = decodeVarint(LVBytes);
        if (vLen < 0 || vLen > afterBytes.length - 1 - varintLength(afterBytes[1])) {
            return false;
        }
        // 尝试将 value 作为 UTF-8 字符串解码
        try {
            String str = new String(afterBytes,"UTF-8");
            // 这里可以添加额外的检查，例如判断字符串是否包含有效的 ASCII 字符
            // 或者使用正则表达式匹配预期的字符串模式
            if (isValidString(str)) {
                return false; // 可能是字符串数据
            }
        } catch (Exception e) {
            // 解码失败，可能是 Protobuf 对象数据
        }
        //这种方式不行，因为 一些字符串也会被识别为对象  "h"左5Bit=13,右3Bit=0(VINT)  "u"左5Bit=14,右3Bit=5(BIT32)
//        if (WireType.hasWireType(wireTypeVal)) {
//            return true;
//        }
        return WireType.hasWireType(wireTypeVal);//保底 可以用这个
    }


    // 变长整数解码（简化版）
    public static int decodeVarint(byte[] data) {
        int result = 0;
        int shift = 0;
        for (byte b : data) {
            int value = b & 0x7F;
            result |= value << shift;
            if ((b & 0x80) == 0) {
                break;
            }
            shift += 7;
        }
        return result;
    }

    // 计算变长整数的字节长度（简化版）
    public static int varintLength(byte b) {
        if ((b & 0x80) == 0) {
            return 1;
        }
        if ((b & 0xC0) == 0x80) {
            return 2;
        }
        if ((b & 0xE0) == 0xC0) {
            return 3;
        }
        if ((b & 0xF0) == 0xE0) {
            return 4;
        }
        if ((b & 0xF8) == 0xF0) {
            return 5;
        }
        return 6; // 理论上最多6个字节，但 Protobuf 中不太可能使用这么多
    }
    // 额外的字符串有效性检查（示例）
    private static boolean isValidString(String str) {
        // 这里可以根据需要添加具体的检查逻辑
        // 例如，判断字符串是否只包含字母、数字、空格和标点符号等
//        boolean isFirstStr = isFirstStr(str);
//        boolean isPrint = isPrintableString(str);
//        System.out.printf("isFirstStr:%s,isPrint:%s%n", isFirstStr, isPrint);
        return isFirstStr(str) || isPrintableString(str);
    }

    public static boolean isFirstStr(String str) {
        char first = str.charAt(0);
        //判断是不是字符串,纯属吓唬猜。
        if (CharUtil.isLetter(first) || '\\' == first || '￨' == first) {
            return true;
        }
        if (first >= 'A' && first <= 'Z' || first >= 'a' && first <= 'z' || first == '_') {
            return true;
        }
        return false;
    }

    // 检查字符串是否只包含可打印字符
    private static boolean isPrintableString(String str) {
        for (int i = 0; i < str.length(); i++) {
            if (!CharUtil.isAsciiPrintable(str.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    public static int level = 0;
    /**
     * protobuf byte[]字段,
     */
    public List<PBField> toFieldList() {
        if (this.offset > this.buffer.length || this.buffer.length < 1) {
            return new ArrayList<>();
        }
        level++;
//        System.out.printf("编号:%s,size:%s,buffer:%s%n", level, this.buffer.length, HexUtil.encodeHexStr(this.buffer));
        byte tryTag = this.buffer[0];
        boolean isNormal = checkTag(tryTag, 0);
        if (isNormal) {
            List<PBField> PBFieldList = new ArrayList<>();
            offset = 0;
            int fieldNumber = 0;

            while (this.offset < this.buffer.length) {
                int tag = this.readRawVarInt32();
                fieldNumber = bitFieldNumber(tag);
                int bitWireType = bitWireType(tag);
                WireType wireType = WireType.valueOf(bitWireType);
                int type = wireType.getType();
                int length = 0;
                Object val = null;
                if (wireType == WireType.VINT) {
                    val = readRawVarInt64();//按最大的读取
                    length = val.toString().length();
                } else if (wireType == WireType.BIT64) {
                    int readLen = WireType.BIT64.getLen();
                    byte[] sub = sub(buffer, offset, offset + readLen);
                    val = Double.longBitsToDouble(bytesToLong(sub, 0, ByteOrder.LITTLE_ENDIAN)); //从buffer中读读取固定8个字节 转为Number类型
                    offset += readLen;
                    length = readLen;
                } else if (wireType == WireType.OBJ_STR) {
                    int readLen = readRawVarInt32();
                    if (readLen == 0) {//空字符串情况
                        val = "";
                    } else {
                        //TLV
                        byte[] sub = sub(buffer, offset, offset + readLen);//OBJ_OR_STR

                        //直接判断对象
                        if (isObjData(sub)) {//判断嵌套对象
                            val = DataBuffer.of(sub).toFieldList();
                        } else {//字符串
                            val = StrUtil.str(sub, "UTF-8");
                            type = 6;
                        }

                        //先判断字符串，再判断对象(官方方式)
//                        if (isValidUTF8(sub)) {
//                            val = StrUtil.str(sub, "UTF-8");
//                            type = 6;
//                        } else if (isObjData(sub)) {//判断嵌套对象
//                            val = DataBuffer.of(sub).toFieldList();
//                        } else {//字符串
//                            val = StrUtil.str(sub, "UTF-8");
//                            type = 6;
//                        }
                    }
                    offset += readLen;
                    length = readLen;
                } else if (wireType == WireType.START || wireType == WireType.END) {
                    //不处理
                    continue;
                } else if (wireType == WireType.BIT32) {
                    //读取4个字节 转为整数
                    int readLen = WireType.BIT32.getLen();
                    byte[] sub = sub(buffer, offset, offset + readLen);
                    val = Float.intBitsToFloat(bytesToInt(sub, 0, ByteOrder.LITTLE_ENDIAN));
                    offset += readLen;
                    length = readLen;
                }
                PBField PBField = new PBField();
                PBField.setFieldNumber(fieldNumber);
                PBField.setType(type);//6为字符串，2为对象，List和Map也为7
                PBField.setLength(length);
                PBField.setVal(val);
                PBFieldList.add(PBField);
            }
            return PBFieldList;
        } else {//list 或 map
            List<PBField> PBFieldList = new ArrayList<>();
            offset = 0;
            int fieldNumber = 0;
            while (this.offset < this.buffer.length) {
                fieldNumber++;
                int oldOffset = this.offset;
                long val = this.readRawVarInt64();
                int newOffset = this.offset;
                PBField PBField = new PBField();
                PBField.setFieldNumber(fieldNumber);
                PBField.setType(7);//7 List map
                PBField.setLength(newOffset - oldOffset);
                PBField.setVal(val);
                PBFieldList.add(PBField);
            }
            return PBFieldList;
        }
    }


    /**
     * UTF-8 字符串是否有效
     *
     * @param bytes 数据
     */
    public static boolean isValidUTF8(byte[] bytes) {
        try {
            // 尝试解码回字符串
            String decoded = new String(bytes, StandardCharsets.UTF_8);
            return true;// 比较解码后的字符串与原字符串
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 转为为map
     *
     * @return TreeMap map
     */
    public TreeMap<String, Object> toMap() {
        TreeMap<String, Object> objMap = new TreeMap<>();
        for (PBField pbField : toFieldList()) {
            handlerField(objMap, pbField);
        }
        return objMap;
    }

    /**
     * 转化为目标对象
     *
     * @param clazz 目标对象
     * @return T 目标对象
     */
    public <T> T toObject(Class<T> clazz) {
        try {
            Constructor<T> constructor = clazz.getConstructor();
            T obj = constructor.newInstance();
            for (PBField pbField : toFieldList()) {
                handlerField(obj, pbField);
            }
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            //忽略
            return null;
        }
    }


    //处理protobuf字段 往map中设置
    private static void handlerField(TreeMap<String, Object> objMap, PBField pbField) {
        if (WireType.hasWireType(pbField.getType())) {
            WireType wireType = WireType.valueOf(pbField.getType());
            if (wireType == WireType.OBJ_STR && pbField.getVal() instanceof List) {//Object类型里的List是子PBField
                List<PBField> array = (List<PBField>) pbField.getVal();
                TreeMap<String, Object> innerObjMap = new TreeMap<>();
                for (PBField field : array) {
                    handlerField(innerObjMap, field);
                }
                String key = String.format("%02d_%s", pbField.getFieldNumber(), wireType.getName());
                objMap.put(key, innerObjMap);
            } else {//PBField 基本类型
                String key = String.format("%02d_%s", pbField.getFieldNumber(), wireType.getName());
                objMap.put(key, pbField.getVal());
            }
        } else if (6 == pbField.getType()) {//字符串
            String key = String.format("%02d_STR", pbField.getFieldNumber());
            objMap.put(key, pbField.getVal());
        } else {//listOrMap
            String key = String.format("%02d_List", pbField.getFieldNumber());
            List<PBField> array = (List<PBField>) pbField.getVal();
            TreeMap<String, Object> innerObjMap = new TreeMap<>();
            for (PBField field : array) {
                String innerKey = String.format("%02d_Ele", pbField.getFieldNumber());
                innerObjMap.put(innerKey, field.getVal());
            }
            objMap.put(key, innerObjMap);
        }
    }

    //处理protobuf字段 往类中设置
    private static <T> void handlerField(T object, PBField pbField) {
        try {
            Field objField = object.getClass().getDeclaredFields()[(int) pbField.getFieldNumber() - 1];
            objField.setAccessible(true);
            WireType wireType = WireType.valueOf(pbField.getType());
            if (wireType == WireType.OBJ_STR && pbField.getVal() instanceof List && !objField.getClass().isPrimitive()) {
                List array = (List) pbField.getVal();
                Object innerObj = objField.getType().getConstructor().newInstance();
                for (Object obj : array) {
                    if (obj instanceof PBField) {
                        PBField field = (PBField) obj;
                        handlerField(innerObj, field);
                    }
                }
                objField.set(object, innerObj);
            } else {
                Class<?> type = objField.getType();
                if (boolean.class.equals(type) || Boolean.class.equals(type)) {
                    objField.set(object, "1".equals(pbField.getVal().toString()));
                } else if (byte.class.equals(type) || Byte.class.equals(type)) {
                    objField.set(object, Byte.parseByte(pbField.getVal().toString()));
                } else if (char.class.equals(type) || Character.class.equals(type)) {
                    objField.set(object, pbField.getVal().toString().charAt(0));
                } else if (short.class.equals(type) || Short.class.equals(type)) {
                    objField.set(object, Short.parseShort(pbField.getVal().toString()));
                } else if (int.class.equals(type) || Integer.class.equals(type)) {
                    objField.set(object, Integer.parseInt(pbField.getVal().toString()));
                } else if (long.class.equals(type) || Long.class.equals(type)) {
                    objField.set(object, Long.parseLong(pbField.getVal().toString()));
                } else if (float.class.equals(type) || Float.class.equals(type)) {
                    objField.set(object, Float.parseFloat(pbField.getVal().toString()));
                } else if (double.class.equals(type) || Double.class.equals(type)) {
                    objField.set(object, Double.parseDouble(pbField.getVal().toString()));
                } else if (String.class.equals(type)) {
                    objField.set(object, pbField.getVal().toString());
                } else if (type.isEnum()) {
                    objField.set(object, Enum.valueOf((Class<Enum>) type, pbField.getVal().toString()));
                } else if (type.isArray()) {
                    objField.set(object, pbField.getVal());
                } else {
                    if (List.class.isAssignableFrom(type)) {
                        List list = (List) objField.get(object);
                        Type genericType = objField.getGenericType();
                        if (genericType instanceof ParameterizedType) {// 如果是泛型参数的类型
                            ParameterizedType pt = (ParameterizedType) genericType;
                            Type actualTypeArgument = pt.getActualTypeArguments()[0];//得到泛型里的class类型对象
                            String typeName = actualTypeArgument.getTypeName().replace("$", ".");
                            Class<?> listTypeClass = Class.forName(typeName);
                            if (boolean.class.equals(listTypeClass) || Boolean.class.equals(listTypeClass)) {
                                boolean equals = "1".equals(pbField.getVal().toString());
                                list.add(equals);
                            } else if (byte.class.equals(listTypeClass) || Byte.class.equals(listTypeClass)) {
                                list.add(Byte.parseByte(pbField.getVal().toString()));
                            } else if (char.class.equals(listTypeClass) || Character.class.equals(listTypeClass)) {
                                list.add(pbField.getVal().toString().charAt(0));
                            } else if (short.class.equals(listTypeClass) || Short.class.equals(listTypeClass)) {
                                list.add(Short.parseShort(pbField.getVal().toString()));
                            } else if (int.class.equals(listTypeClass) || Integer.class.equals(listTypeClass)) {
                                list.add(Integer.parseInt(pbField.getVal().toString()));
                            } else if (long.class.equals(listTypeClass) || Long.class.equals(listTypeClass)) {
                                list.add(Long.parseLong(pbField.getVal().toString()));
                            } else if (float.class.equals(listTypeClass) || Float.class.equals(listTypeClass)) {
                                list.add(Float.parseFloat(pbField.getVal().toString()));
                            } else if (double.class.equals(listTypeClass) || Double.class.equals(listTypeClass)) {
                                list.add(Double.parseDouble(pbField.getVal().toString()));
                            } else if (String.class.equals(listTypeClass)) {
                                list.add(pbField.getVal().toString());
                            }
                        }
                        objField.set(object, list);
                    } else {
                        objField.set(object, pbField.getVal());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }

    private static boolean debug = false;

    private static void printArr(int fieldNumber, WireType wireType, byte[] sub) {
        if (debug) {
            String str = String.format("%s|%s|%s", fieldNumber, wireType.getName(), Arrays.toString(sub));
            System.out.println(str);
        }
    }


    private int bitFieldNumber(int tag) {
        return tag >>> 3;
    }

    private int bitWireType(int tag) {
        return tag & 0x07;
    }


    //VarInt读4位
    private int readRawVarInt32() {
        byte tmp = buffer[offset++];
        if (tmp >= 0) {
            return tmp;
        } else {
            int result = tmp & 127;
            if ((tmp = buffer[offset++]) >= 0) {
                result |= tmp << 7;
            } else {
                result |= (tmp & 127) << 7;
                if ((tmp = buffer[offset++]) >= 0) {
                    result |= tmp << 14;
                } else {
                    result |= (tmp & 127) << 14;
                    if ((tmp = buffer[offset++]) >= 0) {
                        result |= tmp << 21;
                    } else {
                        result |= (tmp & 127) << 21;
                        result |= (tmp = buffer[offset++]) << 28;
                        if (tmp < 0) {
                            for (int i = 0; i < 5; ++i) {
                                if (buffer[offset++] >= 0) {
                                    return result;
                                }
                            }
                            throw new RuntimeException("RuntimeException:readRawVarInt32");
                        }
                    }
                }
            }
            return result;
        }
    }


    //VarInt读8位
    private long readRawVarInt64() {
        int shift = 0;
        for (long result = 0L; shift < 64; shift += 7) {
            byte b = this.buffer[this.offset++];
            result |= (long) (b & 127) << shift;
            if ((b & 128) == 0) {
                return result;
            }
        }
        throw new RuntimeException("RuntimeException:readRawVarInt64");
    }


    private static byte[] sub(byte[] array, int start, int end) {
        int length = Array.getLength(array);
        if (start < 0) {
            start += length;
        }
        if (end < 0) {
            end += length;
        }
        if (start == length) {
            return new byte[0];
        }
        if (start > end) {
            int tmp = start;
            start = end;
            end = tmp;
        }
        if (end > length) {
            if (start >= length) {
                return new byte[0];
            }
            end = length;
        }
        return Arrays.copyOfRange(array, start, end);
    }


    public static long bytesToLong(byte[] bytes, int start, ByteOrder byteOrder) {
        long values = 0;
        if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
            for (int i = (Long.BYTES - 1); i >= 0; i--) {
                values <<= Byte.SIZE;
                values |= (bytes[i + start] & 0xff);
            }
        } else {
            for (int i = 0; i < Long.BYTES; i++) {
                values <<= Byte.SIZE;
                values |= (bytes[i + start] & 0xff);
            }
        }

        return values;
    }


    public static int bytesToInt(byte[] bytes, int start, ByteOrder byteOrder) {
        if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
            return bytes[start] & 0xFF | //
                    (bytes[1 + start] & 0xFF) << 8 | //
                    (bytes[2 + start] & 0xFF) << 16 | //
                    (bytes[3 + start] & 0xFF) << 24; //
        } else {
            return bytes[3 + start] & 0xFF | //
                    (bytes[2 + start] & 0xFF) << 8 | //
                    (bytes[1 + start] & 0xFF) << 16 | //
                    (bytes[start] & 0xFF) << 24; //
        }

    }
}
